﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity;
using System.Linq;
using System.Transactions;
using MiniStock.BusinessLogic.EventQueue.Data;
using MiniStock.Entities.Common.Events;
using MiniStock.Infrastructure.Configurator;
using MiniStock.Infrastructure.Queue;

namespace MiniStock.BusinessLogic.EventQueue.Queue
{
    public class DbTableQueue : IEventQueue
    {
        public string TableName { get; set; }

        static DbTableQueue()
        {
            Database.SetInitializer(new EventDispatcherContextInitializer());
        }

        public DbTableQueue()
        {
            TableName = ConfigurationManager.AppSettings["DbTableQueue.TableName"];
            if (String.IsNullOrEmpty(TableName)) TableName = "DbTableQueue";

            using (var context = new EventDispatcherContext())
            {
                context.Database.CreateIfNotExists();
                context.Database.Initialize(false);
            }
        }

        #region IEventQueue Members

        public string Address { get; set; }

        public EventBase Dequeue(string clientHash = null)
        {
            EventQueueEventData result = null;

            using (var ts = new TransactionScope())
            using (var context = new EventDispatcherContext())
            {
                result =
                    context.Events.Include("Affinity").Where(
                        i => i.State == "New" && (i.Affinity.ClientHash == null || i.Affinity.ClientHash == clientHash)).OrderBy(
                            i => i.EventDate).
                        FirstOrDefault();

                if (result == null)
                    return null;

                result.State = "Submitted";
                result.SubmittedDate = DateTime.Now;
                result.SubmittedTo = clientHash;
                result.Affinity.ClientHash = clientHash;

                context.SaveChanges();
                ts.Complete();
            }

            return result.ContentXml.ToEventBase();
        }

        public void Enqueue(EventBase item)
        {
            var result = new EventQueueEventData
                             {
                                 Id = Guid.NewGuid(),
                                 EventId = item.EventId,
                                 EntityHash = item is ProtocolEventBase ? null : ((EntityEventBase)item).GetEntityHash(),
                                 EventDate = item.EventDate,
                                 //SubmittedTo = item.OutQueueAddress,
                                 State = "New",
                                 ContentXml = item.ToXmlString(),
                             };

            using (var ts = new TransactionScope())
            using (var context = new EventDispatcherContext())
            {
                context.Events.Add(result);
                result.Affinity = context.ClientAffinity.FirstOrDefault(i => i.EntityHash == result.EntityHash);
                if (result.Affinity == null) result.Affinity = new EventQueueHashAffinity { EntityHash = result.EntityHash, };
                
                context.SaveChanges();
                ts.Complete();
            }
        }

        public bool HasEvent()
        {
            using (var context = new EventDispatcherContext())
            {
                var result = context.Events.Any(i => i.State == "New");
                return result;
            }
        }

        public void Configure(IEventQueueConfigurator configurator, string queueName)
        {
            throw new NotImplementedException();
        }

        public void CleanupClientAffinity(string clientHash)
        {
            using (var ts = new TransactionScope())
            using (var context = new EventDispatcherContext())
            {
                context.ClientAffinity.Where(i => i.ClientHash == clientHash).ToList().ForEach(i => i.ClientHash = null);
                context.SaveChanges();
                ts.Complete();
            }
        }

        public List<EventBase> CollectEvents(DateTime from, string entityType = null, string entityHash = null)
        {
            using (var context = new EventDispatcherContext())
            {
                var query = context.Events.Where(i => i.EventDate >= from && i.SubmittedTo == null);
                if (entityHash != null) query = query.Where(i => i.EntityHash == null || i.EntityHash == entityHash);
                else if (entityType != null) query = query.Where(i => i.EntityHash == null || i.EntityHash.StartsWith(entityType));

                var result =
                    query.OrderBy(i => i.EventDate).ToList().Select(i => i.ContentXml.ToEventBase()).ToList();
                return result;
            }
        }

        public IEnumerable<EventBase> AsEnumerable()
        {
            using (var context = new EventDispatcherContext())
            {
                var result =
                    context.Events.Where(i => i.State == "New").Select(i => i.ContentXml.ToEventBase()).
                        AsEnumerable();
                return result;
            }

        }

        public int Count()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
